package cn.iocoder.yudao.module.hs.service.entryorder;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.hs.controller.admin.bigscreen.vo.WarehouseMaterialCount;
import cn.iocoder.yudao.module.hs.controller.admin.entryorder.vo.*;
import cn.iocoder.yudao.module.hs.controller.admin.receiveorder.vo.ReceiveOrderSaveReqVO;
import cn.iocoder.yudao.module.hs.controller.admin.rfid.vo.RfidSaveReqVO;
import cn.iocoder.yudao.module.hs.controller.admin.shiporder.vo.ShipOrderSaveReqVO;
import cn.iocoder.yudao.module.hs.dal.dataobject.device.DeviceDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.entryorder.EntryOrderDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.entryorderdetail.EntryOrderDetailDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.material.MaterialDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.materialmode.MaterialModeDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.picktaskmaterialdetail.PickTaskMaterialDetailDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.qcorderdetail.QcOrderDetailDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.receiveorder.ReceiveOrderDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.receiveorderdetail.ReceiveOrderDetailDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.rfid.RfidDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.shiporder.ShipOrderDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.tray.TrayDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.warehouse.WarehouseDO;
import cn.iocoder.yudao.module.hs.dal.mysql.entryorder.EntryOrderMapper;
import cn.iocoder.yudao.module.hs.dal.mysql.entryorderdetail.EntryOrderDetailMapper;
import cn.iocoder.yudao.module.hs.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.hs.dal.redis.no.OrderNoRedisDAO;
import cn.iocoder.yudao.module.hs.enums.entryorder.EntryTypeEnum;
import cn.iocoder.yudao.module.hs.enums.entryorder.QuickEntryOrderEnum;
import cn.iocoder.yudao.module.hs.enums.material.MaterialOperateTypeEnum;
import cn.iocoder.yudao.module.hs.enums.materialmode.MaterialModeConstants;
import cn.iocoder.yudao.module.hs.enums.qcorder.QcResultEnum;
import cn.iocoder.yudao.module.hs.enums.receiveorder.ReceiveTypeEnum;
import cn.iocoder.yudao.module.hs.enums.shiporder.ShipOrderStatusEnum;
import cn.iocoder.yudao.module.hs.enums.warehouse.WarehouseStatusEnum;
import cn.iocoder.yudao.module.hs.enums.warehouse.WarehouseTypeEnum;
import cn.iocoder.yudao.module.hs.framework.core.annotations.MaterialLog;
import cn.iocoder.yudao.module.hs.framework.core.utils.MaterialLogUtils;
import cn.iocoder.yudao.module.hs.netty.RFIDData;
import cn.iocoder.yudao.module.hs.service.device.DeviceService;
import cn.iocoder.yudao.module.hs.service.material.MaterialService;
import cn.iocoder.yudao.module.hs.service.materialbox.MaterialBoxService;
import cn.iocoder.yudao.module.hs.service.materialmode.MaterialModeService;
import cn.iocoder.yudao.module.hs.service.picktaskmaterialdetail.PickTaskMaterialDetailService;
import cn.iocoder.yudao.module.hs.service.qcorderdetail.QcOrderDetailService;
import cn.iocoder.yudao.module.hs.service.receiveorder.ReceiveOrderService;
import cn.iocoder.yudao.module.hs.service.receiveorderdetail.ReceiveOrderDetailService;
import cn.iocoder.yudao.module.hs.service.rfid.RfidService;
import cn.iocoder.yudao.module.hs.service.shiporder.ShipOrderService;
import cn.iocoder.yudao.module.hs.service.tray.TrayService;
import cn.iocoder.yudao.module.hs.service.warehouse.WarehouseService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.hs.enums.ErrorCodeConstants.*;

/**
 * 入库单 Service 实现类
 *
 * @author 航盛
 */
@Service
@Validated
@Slf4j
public class EntryOrderServiceImpl implements EntryOrderService {

    @Resource
    private EntryOrderMapper entryOrderMapper;
    @Resource
    private EntryOrderDetailMapper entryOrderDetailMapper;
    @Resource
    private OrderNoRedisDAO orderNoRedisDAO;
    @Resource
    private TrayService trayService;
    @Resource
    private MaterialBoxService materialBoxService;
    @Resource
    private MaterialService materialService;
    @Resource
    private DeviceService deviceService;
    @Resource
    private PickTaskMaterialDetailService pickTaskMaterialDetailService;
    @Resource
    private ShipOrderService shipOrderService;
    @Resource
    private WarehouseService warehouseService;
    @Resource
    private RfidService rfidService;
    @Resource
    private ReceiveOrderService receiveOrderService;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private QcOrderDetailService qcOrderDetailService;
    @Resource
    private MaterialModeService materialModeService;
    @Autowired
    private ReceiveOrderDetailService receiveOrderDetailService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @MaterialLog(operateType = MaterialOperateTypeEnum.PROD_ENTRY)
    public Long createEntryOrder(EntryOrderSaveReqVO createReqVO) {
        TrayDO trayDO = trayService.getTray(createReqVO.getTrayCode());
        if (ObjectUtil.isNull(trayDO)) {
            throw exception(TRAY_NOT_EXISTS);
        }
        // 校验托盘是否已经入库
        if (ObjectUtil.notEqual(WarehouseStatusEnum.WAIT.getStatus(), trayDO.getStatus())) {
            throw exception(TRAY_WAREHOUSE_EXISTS);
        }
        WarehouseDO warehouse = warehouseService.getWarehouse(trayDO.getWarehouseId());
        if (warehouse == null) {
            throw exception(WAREHOUSE_NOT_EXISTS);
        }
        RFIDData rfidData = RFIDData.getRFIDData(createReqVO.getRfidCode());
        if (rfidData == null) {
            throw exception(RFID_NOT_FOUND);
        }
        if (rfidData.getIsLow().equals(1)) {
            throw exception(RFID_LOW_BATTERY);
        }
        EntryOrderDO entryOrder = buildEntryOrder(warehouse);
        entryOrderMapper.insert(entryOrder);
        // 查询托盘下所有物料
        List<MaterialDO> materialList = materialService.getMaterialListByTrayCode(createReqVO.getTrayCode());
        // 入库单明细
        List<EntryOrderDetailDO> orderDetails = new ArrayList<>();
        materialList.forEach(materialDO -> orderDetails.add(buildOrderDetail(entryOrder.getId(), materialDO,
                createReqVO.getRfidCode(), trayDO.getWarehouseId())));
        entryOrderDetailMapper.insertBatch(orderDetails);
        // 创建RFID，并绑定托盘编码
        buildRfid(createReqVO, entryOrder);
        // 更新托盘入库信息
        trayService.updateTrayEntryByCode(createReqVO.getTrayCode(), createReqVO.getRfidCode(), trayDO.getWarehouseId(), warehouse.getDeptId());
        // 更新托盘对应的物料箱信息
        materialBoxService.updateMaterialBoxEntryByTrayCode(createReqVO.getTrayCode(), trayDO.getWarehouseId(), warehouse.getDeptId());
        // 更新托盘对应的物料信息
        materialService.updateMaterialEntryByTrayCode(createReqVO.getTrayCode(), trayDO.getWarehouseId(), warehouse.getDeptId(), WarehouseTypeEnum.PRODUCTION.getType());
        // 更新入库单为已入库
        entryOrder.setStatus(WarehouseStatusEnum.ENTRY.getStatus());
        entryOrderMapper.updateById(entryOrder);
        // 记录物料日志
        MaterialLogUtils.setMaterialInfo(materialList.stream().map(MaterialDO::getId).collect(Collectors.toList()));
        // 返回
        return entryOrder.getId();
    }

    /**
     * 构建RFID信息
     *
     * @param createReqVO
     * @param entryOrder
     * @return
     */
    private void buildRfid(EntryOrderSaveReqVO createReqVO, EntryOrderDO entryOrder) {
        RfidDO rfid = rfidService.getRfid(createReqVO.getRfidCode());
        if (rfid != null) {
            if (!createReqVO.getTrayCode().equals(rfid.getTrayCode()) && StrUtil.isNotEmpty(rfid.getTrayCode())) {
                throw exception(RFID_ALREADY_BOUND);
            }
            if (rfid.getWarehousingTime() != null) {
                throw exception(RFID_ALREADY_ENTRY);
            }
            rfid.setTrayCode(createReqVO.getTrayCode());
            rfid.setWarehousingTime(entryOrder.getEntryTime());
            rfidService.updateRfid(BeanUtils.toBean(rfid, RfidSaveReqVO.class));
        } else {
            RfidSaveReqVO rfidSaveReqVO = new RfidSaveReqVO();
            rfidSaveReqVO.setCode(createReqVO.getRfidCode());
            rfidSaveReqVO.setLowBattery(false);
            rfidSaveReqVO.setTrayCode(createReqVO.getTrayCode());
            rfidSaveReqVO.setWarehousingTime(entryOrder.getEntryTime());
            rfidService.createRfid(rfidSaveReqVO);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @MaterialLog(operateType = MaterialOperateTypeEnum.THIRD_ENTRY)
    public void createTripartiteEntryOrder(TripartiteEntryOrderSaveReqVO createReqVO) {
        Long warehouseId = null;
        String entryType = null;
        if (StrUtil.isNotEmpty(createReqVO.getDeviceCode())) {
            // 查询基站信息
            DeviceDO deviceDO = deviceService.getDeviceByCode(createReqVO.getDeviceCode());
            if (ObjectUtil.isNotNull(deviceDO)) {
                warehouseId = deviceDO.getWarehouseId();
            }
            entryType = EntryTypeEnum.DEVICE.getName();
        } else {
            AdminUserRespDTO user = adminUserApi.getUser(getLoginUserId());
            if (user != null) {
                warehouseId = user.getWarehouseId();
            }
            entryType = EntryTypeEnum.MANUAL.getName();
        }
        WarehouseDO warehouse = warehouseService.getWarehouse(warehouseId);
        if (warehouse == null) {
            throw exception(WAREHOUSE_NOT_EXISTS);
        }
        if (!WarehouseTypeEnum.TRIPARTITE.getType().equals(warehouse.getWarehouseType())) {
            throw exception(WAREHOUSE_TYPE_ERROR);
        }
        // 查询托盘信息
        TrayDO tray = trayService.getTray(createReqVO.getCode());
        List<MaterialDO> materialList = null;
        RfidDO rfid = null;
        if (tray != null) {
            // 查询托盘下所有物料
            materialList = materialService.getMaterialListByTrayCode(tray.getCode());
        }
        if (CollectionUtil.isEmpty(materialList)) {
            rfid = rfidService.getRfid(createReqVO.getCode());
            if (rfid == null) {
                throw exception(TRAY_RFID_NOT_EXISTS);
            }
            tray = trayService.getTrayByRfidCode(rfid.getCode());
            if (tray == null) {
                throw exception(TRAY_RFID_NOT_EXISTS);
            }
            // 查询托盘下所有物料
            materialList = materialService.getMaterialListByTrayCode(tray.getCode());
            if (CollectionUtil.isEmpty(materialList)) {
                throw exception(TRAY_RFID_MATERIAL_EMPTY);
            }
        }
        PickTaskMaterialDetailDO pickTaskMaterialDetail = pickTaskMaterialDetailService.getPickTaskMaterialDetail(materialList.get(0).getCode());
        if (pickTaskMaterialDetail == null) {
            throw exception(PICK_TASK_MATERIAL_DETAIL_NOT_EXISTS);
        }
        ShipOrderDO shipOrder = shipOrderService.getShipOrder(pickTaskMaterialDetail.getShipOrderId());
        if (ObjectUtil.isNull(shipOrder)) {
            throw exception(SHIP_ORDER_NOT_EXISTS);
        }
        if (!warehouse.getId().equals(shipOrder.getToWarehouseId())) {
            throw exception(ENTRY_ORDER_WAREHOUSE_ERROR);
        }
        ReceiveOrderDO receiveOrder = receiveOrderService.getReceiveOrderByShipOrderId(shipOrder.getId());
        if (receiveOrder == null) {
            throw exception(ENTRY_ORDER_NOT_RECEIVE, tray.getCode());
        }
        List<ReceiveOrderDetailDO> receiveOrderDetailList = receiveOrderDetailService.getReceiveOrderDetailList(tray.getCode());
        if (CollUtil.isEmpty(receiveOrderDetailList)) {
            throw exception(ENTRY_ORDER_NOT_RECEIVE_DETAIL, tray.getCode());
        }
        List<EntryOrderDetailDO> entryOrderDetailDOS = entryOrderDetailMapper.selectList(EntryOrderDetailDO::getTrayCode, tray.getCode(), EntryOrderDetailDO::getWarehouseId, warehouse.getId());
        if (CollectionUtil.isNotEmpty(entryOrderDetailDOS)) {
            throw exception(ENTRY_ORDER_TRAY_EXISTS);
        }
        int noQcCount = 0;
        for (MaterialDO material : materialList) {
            EntryOrderDetailDO entryOrderDetailDO = entryOrderDetailMapper.selectOne(EntryOrderDetailDO::getMaterialCode, material.getCode(), EntryOrderDetailDO::getWarehouseId, warehouse.getId());
            if (ObjectUtil.isNotNull(entryOrderDetailDO)) {
                throw exception(ENTRY_ORDER_MATERIAL_EXISTS);
            }
            // 根据产品码查询出库单拣货物料
            PickTaskMaterialDetailDO pickTaskMaterialDetailDO = pickTaskMaterialDetailService.getPickTaskMaterialDetail(material.getCode());
            if (ObjectUtil.isNull(pickTaskMaterialDetailDO)) {
                throw exception(PICK_TASK_MATERIAL_DETAIL_NOT_EXISTS);
            }
            MaterialModeDO materialMode = null;
            if (EntryTypeEnum.DEVICE.getName().equals(entryType)) {
                materialMode = materialModeService.getMaterialMode(material.getModeCode(), warehouse.getId());
            }
            if (ObjectUtil.isNotNull(materialMode) && MaterialModeConstants.LANDING_QC_YES.equals(materialMode.getLandingQc())) {
                if (StrUtil.isEmpty(material.getQcResult())) {
                    noQcCount++;
                }
            }
        }
        if (noQcCount > 0) {
            throw exception(ENTRY_ORDER_NEED_QC);
        }
        // 入库
        materialEntry(materialList, tray, warehouse, shipOrder, QuickEntryOrderEnum.NORMAL.getValue(), entryType);
    }

    @Override
    public void createQuickEntryOrder(QuickEntryOrderSaveReqVO createReqVO) {
        AdminUserRespDTO user = adminUserApi.getUser(getLoginUserId());
        WarehouseDO warehouse = warehouseService.getWarehouse(user.getWarehouseId());
        if (warehouse == null) {
            throw exception(WAREHOUSE_NOT_EXISTS);
        }
        if (!WarehouseTypeEnum.TRIPARTITE.getType().equals(warehouse.getWarehouseType())) {
            throw exception(WAREHOUSE_TYPE_ERROR);
        }
        // 查询托盘信息
        TrayDO tray = trayService.getTray(createReqVO.getCode());
        List<MaterialDO> materialList = null;
        RfidDO rfid = null;
        if (tray != null) {
            // 查询托盘下所有物料
            materialList = materialService.getMaterialListByTrayCode(tray.getCode());
        }
        if (CollectionUtil.isEmpty(materialList)) {
            rfid = rfidService.getRfid(createReqVO.getCode());
            if (rfid == null) {
                throw exception(TRAY_RFID_NOT_EXISTS);
            }
            tray = trayService.getTrayByRfidCode(rfid.getCode());
            if (tray == null) {
                throw exception(TRAY_RFID_NOT_EXISTS);
            }
            // 查询托盘下所有物料
            materialList = materialService.getMaterialListByTrayCode(tray.getCode());
            if (CollectionUtil.isEmpty(materialList)) {
                throw exception(TRAY_RFID_MATERIAL_EMPTY);
            }
        }
        List<EntryOrderDetailDO> entryOrderDetailDOS = entryOrderDetailMapper.selectList(EntryOrderDetailDO::getTrayCode, tray.getCode(), EntryOrderDetailDO::getWarehouseId, warehouse.getId());
        if (CollectionUtil.isNotEmpty(entryOrderDetailDOS)) {
            throw exception(ENTRY_ORDER_TRAY_EXISTS);
        }
        ShipOrderDO shipOrder = null;
        for (MaterialDO material : materialList) {
            EntryOrderDetailDO entryOrderDetailDO = entryOrderDetailMapper.selectOne(EntryOrderDetailDO::getMaterialCode, material.getCode(), EntryOrderDetailDO::getWarehouseId, warehouse.getId());
            if (ObjectUtil.isNotNull(entryOrderDetailDO)) {
                throw exception(ENTRY_ORDER_MATERIAL_EXISTS);
            }
            // 根据产品码查询出库单拣货物料
            PickTaskMaterialDetailDO pickTaskMaterialDetail = pickTaskMaterialDetailService.getPickTaskMaterialDetail(material.getCode());
            if (ObjectUtil.isNull(pickTaskMaterialDetail)) {
                throw exception(PICK_TASK_MATERIAL_DETAIL_NOT_EXISTS);
            }
            // 出库单
            shipOrder = shipOrderService.getShipOrder(pickTaskMaterialDetail.getShipOrderId());
            if (ObjectUtil.isNull(shipOrder)) {
                throw exception(SHIP_ORDER_NOT_EXISTS);
            }
            if (!warehouse.getId().equals(shipOrder.getToWarehouseId())) {
                throw exception(ENTRY_ORDER_WAREHOUSE_ERROR);
            }
        }
        // 入库
        materialEntry(materialList, tray, warehouse, shipOrder, QuickEntryOrderEnum.QUICK.getValue(), EntryTypeEnum.MANUAL.getName());
    }

    /**
     * 物料收货
     *
     * @param materialList 物料列表
     */
    private void materialReceive(List<MaterialDO> materialList, WarehouseDO warehouse, Integer isQuickEntry) {
        ReceiveOrderSaveReqVO receiveReqVO = new ReceiveOrderSaveReqVO()
                .setReceiveType(ReceiveTypeEnum.AUTO.getType())
                .setWarehouseId(warehouse.getId())
                .setQuickEntry(isQuickEntry)
                .setCodeList(materialList.stream().map(MaterialDO::getCode).collect(Collectors.toList()));
        receiveOrderService.createReceiveOrder(receiveReqVO);
    }


    /**
     * 物料入库
     *
     * @param materialList 物料列表
     * @param trayDO       托盘信息
     * @param warehouse    仓库信息
     */
    private void materialEntry(List<MaterialDO> materialList, TrayDO trayDO, WarehouseDO warehouse, ShipOrderDO shipOrder, Integer isQuickEntry, String entryType) {
        // 出库单
        List<EntryOrderDO> entryOrderDOS = entryOrderMapper.selectList(EntryOrderDO::getShipOrderId, shipOrder.getId());
        EntryOrderDO entryOrder = null;
        if (CollUtil.isEmpty(entryOrderDOS)) {
            entryOrder = buildEntryOrder(warehouse);
            entryOrder.setShipOrderId(shipOrder.getId());
            entryOrder.setQuickEntry(isQuickEntry);
            entryOrderMapper.insert(entryOrder);
        } else {
            entryOrder = entryOrderDOS.get(0);
        }
        // 先收集所有物料编码
        List<String> materialCodes = materialList.stream()
                .map(MaterialDO::getCode)
                .collect(Collectors.toList());
        // 批量查询已存在的入库单明细
        List<EntryOrderDetailDO> existingDetails = entryOrderDetailMapper.selectList(
                new LambdaQueryWrapperX<EntryOrderDetailDO>()
                        .eq(EntryOrderDetailDO::getOrderId, entryOrder.getId())
                        .in(EntryOrderDetailDO::getMaterialCode, materialCodes)
        );
        // 获取已存在的物料编码集合
        Set<String> existingMaterialCodes = existingDetails.stream()
                .map(EntryOrderDetailDO::getMaterialCode)
                .collect(Collectors.toSet());

        // 过滤出需要新增的物料
        List<MaterialDO> materialsToInsert = materialList.stream()
                .filter(material -> !existingMaterialCodes.contains(material.getCode()))
                .collect(Collectors.toList());
        // 构建新的入库单明细对象集合
        List<EntryOrderDetailDO> orderDetails = new ArrayList<>();
        for (MaterialDO material : materialsToInsert) {
            EntryOrderDetailDO orderDetail = buildOrderDetail(entryOrder.getId(), material, trayDO.getRfidCode(), warehouse.getId());
            orderDetail.setShipOrderId(shipOrder.getId());
            orderDetails.add(orderDetail);
        }
        // 批量插入
        if (CollectionUtil.isNotEmpty(orderDetails)) {
            entryOrderDetailMapper.insertBatch(orderDetails);
        }
        // 出库的产品码
        List<String> shipMaterialCodes = pickTaskMaterialDetailService
                .getPickTaskMaterialDetailByShipOrderId(shipOrder.getId())
                .stream().map(PickTaskMaterialDetailDO::getMaterialCode).collect(Collectors.toList());
        // 入库的产品码
        List<String> entryMaterialCodes = entryOrderDetailMapper
                .selectListByShipOrderId(shipOrder.getId())
                .stream().map(EntryOrderDetailDO::getMaterialCode).collect(Collectors.toList());

        // 校验是否全部入库
        if (CollectionUtils.isEqualCollection(shipMaterialCodes, entryMaterialCodes)) {
            allEntry(trayDO.getCode(), warehouse.getId(), warehouse.getDeptId(), entryOrder, shipOrder);
        } else {
            Set<String> extraMaterialCodes = shipMaterialCodes.stream()
                    .filter(code -> !entryMaterialCodes.contains(code))
                    .collect(Collectors.toSet());
            List<String> qcResults = CollectionUtil.toList(QcResultEnum.DEFECT.getType(), QcResultEnum.BACK.getType());
            List<QcOrderDetailDO> qcOrderDetailList = qcOrderDetailService.getQcOrderDetailList(extraMaterialCodes, qcResults);
            if (qcOrderDetailList.size() == extraMaterialCodes.size()) {
                allEntry(trayDO.getCode(), warehouse.getId(), warehouse.getDeptId(), entryOrder, shipOrder);
            } else {
                doEntry(trayDO.getCode(), warehouse.getId(), warehouse.getDeptId(), shipOrder);
            }
        }
    }

    /**
     * 入库
     *
     * @param trayCode    托盘码
     * @param warehouseId 仓库ID
     * @param deptId      部门ID
     */
    private void doEntry(String trayCode, Long warehouseId, Long deptId, ShipOrderDO shipOrder) {
        // 更新出库单状态为已收货
        shipOrder.setStatus(ShipOrderStatusEnum.ENTRYING.getStatus());
        shipOrderService.updateShipOrder(BeanUtils.toBean(shipOrder, ShipOrderSaveReqVO.class));

        // 更新托盘入库信息
        trayService.updateTrayEntryByCode(trayCode, null, warehouseId, deptId);
        // 更新托盘对应的物料箱信息
        materialBoxService.updateMaterialBoxEntryByTrayCode(trayCode, warehouseId, deptId);
        // 更新托盘对应的物料信息
        materialService.updateMaterialEntryByTrayCode(trayCode, warehouseId, deptId, WarehouseTypeEnum.TRIPARTITE.getType());
    }

    /**
     * 全部入库
     *
     * @param trayCode    托盘码
     * @param warehouseId 仓库ID
     * @param deptId      部门ID
     * @param entryOrder  入库单
     * @param shipOrder   出库单
     */
    private void allEntry(String trayCode, Long warehouseId, Long deptId, EntryOrderDO entryOrder, ShipOrderDO shipOrder) {
        // 全部入库更新入库单状态为已入库
        entryOrder.setStatus(WarehouseStatusEnum.ENTRY.getStatus());
        entryOrder.setEntryTime(LocalDateTime.now());
        entryOrderMapper.updateById(entryOrder);
        // 更新出库单状态为已收货
        shipOrder.setStatus(ShipOrderStatusEnum.ENTRY.getStatus());
        shipOrderService.updateShipOrder(BeanUtils.toBean(shipOrder, ShipOrderSaveReqVO.class));

        // 更新托盘入库信息
        trayService.updateTrayEntryByCode(trayCode, null, warehouseId, deptId);
        // 更新托盘对应的物料箱信息
        materialBoxService.updateMaterialBoxEntryByTrayCode(trayCode, warehouseId, deptId);
        // 更新托盘对应的物料信息
        materialService.updateMaterialEntryByTrayCode(trayCode, warehouseId, deptId, WarehouseTypeEnum.TRIPARTITE.getType());
    }

    /**
     * 构建入库单
     *
     * @param warehouse 仓库
     * @return 入库单
     */
    private EntryOrderDO buildEntryOrder(WarehouseDO warehouse) {
        EntryOrderDO entryOrder = new EntryOrderDO();
        // 入库单号
        entryOrder.setNo(orderNoRedisDAO.generate(OrderNoRedisDAO.ENTRY_ORDER_NO_PREFIX, RedisKeyConstants.PICK_TASK_NO));
        // 所属仓库
        entryOrder.setWarehouseId(warehouse.getId());
        entryOrder.setWarehouseType(warehouse.getWarehouseType());
        entryOrder.setDeptId(warehouse.getDeptId());
        // 状态
        entryOrder.setStatus(WarehouseStatusEnum.ENTRY_ING.getStatus());
        // 操作人
        Long userId = getLoginUserId();
        if (ObjectUtil.isNotNull(userId)) {
            entryOrder.setOperator(userId.toString());
        }
        // 入库时间
        entryOrder.setEntryTime(LocalDateTime.now());
        return entryOrder;
    }

    /**
     * 构建入库单明细
     *
     * @param entryOrderId 入库单ID
     * @param material     物料信息
     * @param rfidCode     rfid编码
     * @param warehouseId  仓库id
     * @return 入库单明细
     */
    private EntryOrderDetailDO buildOrderDetail(Long entryOrderId, MaterialDO material, String rfidCode,
                                                Long warehouseId) {
        EntryOrderDetailDO orderDetail = new EntryOrderDetailDO();
        orderDetail.setOrderId(entryOrderId);
        orderDetail.setTrayCode(material.getTrayCode());
        orderDetail.setBoxCode(material.getBoxCode());
        orderDetail.setMaterialCode(material.getCode());
        orderDetail.setMaterialName(material.getName());
        orderDetail.setModeCode(material.getModeCode());
        orderDetail.setRfidCode(rfidCode);
        orderDetail.setWarehouseId(warehouseId);
        orderDetail.setEntryTime(LocalDateTime.now());
        return orderDetail;
    }

    @Override
    public void updateEntryOrder(EntryOrderSaveReqVO updateReqVO) {
        // 校验存在
        validateEntryOrderExists(updateReqVO.getId());
        // 更新
        EntryOrderDO updateObj = BeanUtils.toBean(updateReqVO, EntryOrderDO.class);
        entryOrderMapper.updateById(updateObj);
    }

    @Override
    public void deleteEntryOrder(Long id) {
        // 校验存在
        validateEntryOrderExists(id);
        // 删除
        entryOrderMapper.deleteById(id);
    }

    private void validateEntryOrderExists(Long id) {
        if (entryOrderMapper.selectById(id) == null) {
            throw exception(ENTRY_ORDER_NOT_EXISTS);
        }
    }

    @Override
    public EntryOrderDO getEntryOrder(Long id) {
        return entryOrderMapper.selectById(id);
    }

    @Override
    public PageResult<EntryOrderRespVO> getEntryOrderPage(EntryOrderPageReqVO pageReqVO) {
        PageResult<EntryOrderDO> entryOrderDOPageResult = entryOrderMapper.selectPage(pageReqVO);
        if (CollUtil.isEmpty(entryOrderDOPageResult.getList())) {
            return PageResult.empty();
        }
        List<AdminUserRespDTO> userList = adminUserApi.getUserList();
        // 获取用户列表并构建 userId -> nickname 的映射
        Map<Long, String> userNicknameMap = userList.stream()
                .collect(Collectors.toMap(AdminUserRespDTO::getId, AdminUserRespDTO::getNickname));

        PageResult<EntryOrderRespVO> entryOrderRespVOPageResult = BeanUtils.toBean(entryOrderDOPageResult, EntryOrderRespVO.class);
        // 遍历 entryOrderDOPageResult.getList() 并赋值 creatorName 和 operatorName
        entryOrderRespVOPageResult.getList().forEach(entryOrderRespVO -> {
            // 匹配 creator 对应的 nickname
            if (entryOrderRespVO.getCreator() != null && userNicknameMap.containsKey(Long.valueOf(entryOrderRespVO.getCreator()))) {
                entryOrderRespVO.setCreatorName(userNicknameMap.get(Long.valueOf(entryOrderRespVO.getCreator())));
            }
            // 匹配 operator 对应的 nickname
            if (StrUtil.isNotEmpty(entryOrderRespVO.getOperator()) && userNicknameMap.containsKey(Long.valueOf(entryOrderRespVO.getOperator()))) {
                entryOrderRespVO.setOperatorName(userNicknameMap.get(Long.valueOf(entryOrderRespVO.getOperator())));
            }
        });
        return entryOrderRespVOPageResult;
    }

    @Override
    public EntryOrderDO getEntryOrderByTrayCode(Long warehouseId, String trayCode) {
        LambdaQueryWrapperX<EntryOrderDetailDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eq(EntryOrderDetailDO::getTrayCode, trayCode)
                .eq(EntryOrderDetailDO::getWarehouseId, warehouseId);
        List<EntryOrderDetailDO> entryOrderDetailDOS = entryOrderDetailMapper.selectList(queryWrapperX);
        if (CollUtil.isEmpty(entryOrderDetailDOS)) {
            return null;
        }
        Long orderId = entryOrderDetailDOS.get(0).getOrderId();
        return getEntryOrder(orderId);
    }

    @Override
    public List<WarehouseMaterialCount> getWarehouseMaterialCountList() {
        return entryOrderMapper.getTripartiteMaterialChart();
    }

    @Override
    public Integer getProductWarehouseHistoryCount(boolean isToday) {
        return entryOrderMapper.getProductWarehouseHistoryCount(isToday);
    }

    @Override
    @DataPermission(enable = false)
    public List<EntryOrderTrayRespVO> getTrayList(Long shipOrderId, Long warehouseId) {
        return entryOrderMapper.getTrayList(shipOrderId, warehouseId);
    }
}
